package net

import (
	"bytes"
	"encoding/binary"
	"smart-flow/server/config"
	"smart-flow/server/logger"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"
)

type HeadPackage struct {
	UiSize    uint32 //消息长度
	UiCmd     uint32 //指令
	UiVersion uint32 //版本号
}

type KeepalivePackage struct {
	HeadPackage
	UiBuffSize uint32 //BUFF的长度
	ChBuff     []byte //BUFF内容
}

type Package struct {
	HeadPackage
	LlTimeStamp       int64  //时间戳
	UTaskID           []byte //任务唯一ID
	UiTaskStatus      uint32 //任务状态
	UiRetCode         uint32 //返回码
	UsIsCompress      uint16 //BUFF是否压缩(0:未压缩, 1:压缩)
	UiPreCompressSize uint32 //压缩前长度(如果usIsCompress=0,则该字段无意义)
	UiToken           uint32 //保留参数1
	UiBuffSize        uint32 //BUFF的长度
	ChBuff            []byte //BUFF内容
}

type CmdAgentLoginReq struct {
	SessionId    string `json:"session_id"`
	Uid          string `json:"uid"`
	ClientPubKey string `json:"client_pub_key"`
}

type CmdAgentLoginRet struct {
	SessionId      string `json:"session_id"`
	Uid            string `json:"uid"`
	Timestamp      string `json:"timestamp"`
	EncryptDataKey string `json:"encrypt_data_key"`
}

type CmdAgentLogoutReq struct {
	SessionId string `json:"session_id"`
	Uid       string `json:"uid"`
	Timestamp int64  `json:"timestamp"`
}

type CmdAgentChannelReq struct {
	SessionId      string `json:"session_id"`
	Uid            string `json:"uid"`
	Timestamp      int64  `json:"timestamp"`
	EncryptDataKey string `json:"encrypt_data_key"`
}

type CmdAgentChannelRet struct {
	SessionId string `json:"session_id"`
	Uid       string `json:"uid"`
	Timestamp string `json:"timestamp"`
}

type CmdAgentChannelAck struct {
	SessionId string `json:"session_id"`
	Uid       string `json:"uid"`
	Timestamp int64  `json:"timestamp"`
}

type CmdAgentExecReq struct {
	SessionId string `json:"session_id"`
	Uid       string `json:"uid"`
	Cmd       string `json:"cmd"`
}

type CmdAgentExecRet struct {
	SessionId string `json:"session_id"`
	Uid       string `json:"uid"`
	Result    string `json:"result"`
}

const HeaderLength = 4

func Unpack(payload *bytes.Buffer, payloadLen uint32) (bool, *Package) {
	//defer func() {
	//	if err := recover(); err != nil {
	//		logger.Logger.Error("[网络] 报文解构错误", zap.Any("err", err))
	//	}
	//}()
	cmd := binary.LittleEndian.Uint32(payload.Next(4))
	if !checkCmd(cmd) {
		logger.Logger.Error("[网络] 未知指令", zap.Uint32("cmd", cmd))
		return false, nil
	}
	if cmd == config.CommonKeepAliveRet {
		return true, nil
	}
	data := new(Package)
	data.UiSize = payloadLen
	data.UiCmd = cmd
	data.UiVersion = binary.LittleEndian.Uint32(payload.Next(4))
	data.LlTimeStamp = int64(binary.LittleEndian.Uint64(payload.Next(8)))
	data.UTaskID = payload.Next(16)
	data.UiTaskStatus = binary.LittleEndian.Uint32(payload.Next(4))
	data.UiRetCode = binary.LittleEndian.Uint32(payload.Next(4))
	data.UsIsCompress = binary.LittleEndian.Uint16(payload.Next(2))
	data.UiPreCompressSize = binary.LittleEndian.Uint32(payload.Next(4))
	data.UiToken = binary.LittleEndian.Uint32(payload.Next(4))
	data.UiBuffSize = binary.LittleEndian.Uint32(payload.Next(4))
	data.ChBuff = payload.Next(int(data.UiBuffSize))
	return true, data
}

func checkCmd(cmd uint32) bool {
	switch cmd {
	case config.CommonClientLoginReq:
		return true
	case config.CommonClientLoginRet:
		return true
	case config.CommonUserLoginReq:
		return true
	case config.CommonUserLoginRet:
		return true
	case config.CommonUserLogoutReq:
		return true
	case config.CommonUserLogoutRet:
		return true
	case config.CommonPushMsgRet:
		return true
	case config.CommonKeepAliveReq:
		return true
	case config.CommonKeepAliveRet:
		return true
	case config.CommonExecCmdReq:
		return true
	case config.CommonExecCmdRet:
		return true
	case config.CommonAgentLost:
		return true
	case config.CommonConsoleLogin:
		return true
	case config.CommonAgentLoginReq:
		return true
	case config.CommonAgentLoginRet:
		return true
	case config.CommonAgentChannelReq:
		return true
	case config.CommonAgentChannelRet:
		return true
	case config.CommonAgentChannelAck:
		return true
	case config.CommonAgentLogoutReq:
		return true
	}
	return false
}

func Pack(cmd uint32, payload *bytes.Buffer, msg []byte) bool {
	var err error
	msgLen := uint32(len(msg))
	if err = binary.Write(payload, binary.LittleEndian, 58+msgLen); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, cmd); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uint32(100)); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, time.Now().Unix()); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uuid.New()); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uint32(0)); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uint32(0)); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uint16(0)); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uint32(0)); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, uint32(0)); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, msgLen); err != nil {
		return false
	}
	if err = binary.Write(payload, binary.LittleEndian, msg); err != nil {
		return false
	}
	return true
}
